Skip to content

Stability fixes - #1574

Open
troglobit wants to merge 10 commits into
mainfrom
stability-fixes
Open

Stability fixes#1574
troglobit wants to merge 10 commits into
mainfrom
stability-fixes

Conversation

@troglobit

@troglobit troglobit commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-up to the statd timeout revert (#1573), fixing the actual root causes behind the regression failures on main, plus related cleanups found along the way.

The meltdown had four layers: statd blocking its event loop (the reverted trigger), the journal snapshot monopolizing the datastore for minutes, mDNS browse storms feeding more contention, and interrupted commits leaving dagger generations half-applied in the kernel, permanently breaking all subsequent interface configuration until reboot.

  • statd: mDNS neighbor updates are debounced into one apply per burst and retried with backoff on datastore contention, the event loop never blocks on a busy datastore
  • statd: journal snapshots run in a forked, low-priority statd-journal process with its own sysrepo connection, reading one module per call so other datastore users interleave. The timer is re-armed only after completion, so snapshots never overlap. Drops the last thread, and the second sysrepo session, from statd
  • confd: interface setup/teardown is now convergent, leftovers from a partially applied generation are removed before link add, and deleting an already-missing interface no longer aborts the generation
  • confd: enabling an optional service not present in the image no longer creates a dangling Finit symlink; build also drops leftover confs (ttyd) and prunes dangling symlinks, silencing minimal builds
  • statd: proper option handling (-h, -V, -v <level>) backed by setlogmask(), defaulting to NOTICE like other services; matching -v info/debug fixes in confd; yanger errors now name the module/interface and exit code
  • mdns-alias v1.3: fixes crash when a hostname change coincides with an avahi restart ("Failed creating new entry group: Bad state")

Checklist

Tick relevant boxes, this PR is-a or has-a:

  • Bugfix
    • Regression tests
    • ChangeLog updates (for next release)
  • Feature
    • YANG model change => revision updated?
    • Regression tests added?
    • ChangeLog updates (for next release)
    • Documentation added?
  • Test changes
    • Checked in changed Readme.adoc (make test-spec)
    • Added new test to group Readme.adoc and yaml file
  • Code style update (formatting, renaming)
  • Refactoring (please detail in commit messages)
  • Build related changes
  • Documentation content changes
    • ChangeLog updated (for major changes)
  • Other (please describe):

@troglobit troglobit added the ci:main Build default defconfig, not minimal label Aug 2, 2026
@troglobit
troglobit requested a review from Copilot August 3, 2026 10:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Stabilizes statd and confd under datastore contention and partial-apply failure modes by reducing event-loop blocking, chunking/isolating operational snapshots, and making interface generation more convergent, plus related build/runtime cleanup.

Changes:

  • statd: debounce mDNS operational datastore applies with retry/backoff; move periodic operational snapshots into a low-priority forked helper process.
  • confd: make interface teardown/create more convergent by tolerating missing links and removing leftovers before link add.
  • Build/ops: skip enabling unavailable optional services, prune dangling Finit symlinks, and bump mdns-alias to v1.3.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/statd/statd.c Adds getopt-based -h/-V/-v handling, adjusts logging defaults, removes extra sysrepo session, wires journal to main ev loop.
src/statd/Makefile.am Injects STATD_VERSION from $(PACKAGE_VERSION) for -V output.
src/statd/journal.h Refactors journal API/types for forked snapshot model and conditional stub builds.
src/statd/journal.c Replaces threaded snapshotting with forked statd-journal child and per-module dump chunking.
src/statd/avahi.h Extends mDNS context with debounce/retry apply timer state.
src/statd/avahi.c Debounces and retries sr_apply_changes() to avoid blocking the event loop under contention.
src/confd/src/main.c Aligns verbosity handling so info maps to LOG_INFO and debug enables debug mode.
src/confd/src/interfaces.c Makes interface deletion tolerant and ensures leftover links are removed before creation.
src/confd/src/core.c Avoids creating dangling Finit enable symlinks for optional services not present in the image.
package/statd/statd.conf Updates service command line to match new statd option handling.
package/mdns-alias/mdns-alias.mk Bumps mdns-alias version to 1.3.
package/mdns-alias/mdns-alias.hash Updates tarball hash for mdns-alias 1.3.
doc/ChangeLog.md Documents the stability fixes and related upgrades.
board/common/post-build.sh Drops optional daemon confs when not built and prunes dangling enabled symlinks.
Suppressed comments (1)

src/statd/journal.c:256

  • w->rstatus is the raw wait(2) status word, so logging it with %d is misleading (e.g., exit code 1 often prints as 256). Log the decoded exit code / signal instead.
	if (!WIFEXITED(w->rstatus) || WEXITSTATUS(w->rstatus))
		ERROR("Journal snapshot failed, status %d", w->rstatus);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/statd/journal.c
Comment thread doc/ChangeLog.md Outdated
Comment thread package/statd/statd.conf
Fixes crash on SIGHUP while disconnected from Avahi, e.g., when the
hostname change that triggered the reload also restarted avahi-daemon:

    mdns-alias[8943]: Failed creating new entry group: Bad state
    finit[1]: Service mdns-alias keeps crashing, not restarting.

Entry group failures, CNAME collisions, and publish errors are now
treated as transient and retried with a full reconnect.  Also quieter
logs by default, routine lifecycle events demoted to INFO.

Release notes: https://github.com/troglobit/mdns-alias/releases/tag/v1.3

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Reading operational data can be slow, or time out entirely, while mDNS
neighbors are being discovered:

    statd[3658]: mdns: sr_apply_changes: Timeout expired
    statd[3658]: Error, getting operational data: User callback failed

statd is single threaded; the avahi handlers apply datastore changes
per resolver event, from the same event loop that serves all
operational get callbacks.  Every apply blocks the loop, and resolver
events arrive in bursts, e.g., after an mDNS daemon restart.

Batch all edits with a debounce timer and apply once the burst has
settled.  On datastore contention, back off and retry later instead of
blocking the loop.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A configuration change that is interrupted, e.g., by a frontend
timeout, can leave interfaces behind in the kernel: the datastore
transaction is rolled back but the dagger generation's side effects
are not.  The next generation then fails creating the interface:

    dagger[2599]: Aborting: /run/net/131/action/init/br0/50-init.ip failed with exitcode 1
    confd[2599]: Failed to apply interface configuration

with 'RTNETLINK answers: File exists' in the script log.  A failed
generation is abandoned, but its executed actions remain, so every
subsequent interface change fails, in either the create or the delete
direction, until reboot.

Delete any leftover before creating a netlink interface, and tolerate
an already-missing interface when deleting one.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Every configuration change that restarts the mDNS daemon logs a NOTICE
level reconnect message.  Routine noise, demote to INFO; the WARN/NOTE
pair for an unresponsive daemon is kept as is.

Also, minor code refactor included, dropping code useless block.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
statd logs everything, including INFO level messages, to syslog by
default, unlike other services which default to NOTICE.

Port option handling from confd: -h, -V, and -v <level>, backed by
setlogmask().  Drop the stale flags from statd.conf, they were never
parsed and are rejected now that statd has strict option handling.

Unlike confd, -v info maps to LOG_INFO rather than LOG_NOTICE, and
-v debug also enables the DEBUG() macro without requiring the DEBUG
environment variable.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Configuring the system, or querying status, can be slow or time out
entirely while the periodic (5 min) journal snapshot is running.  On
slow systems with a big configuration a snapshot can take minutes.

The snapshot ran in a statd thread, reading ALL operational data in a
single sr_get_data("/*") call: every subsystem's callbacks are invoked
back-to-back while datastore locks are held for the entire read, with
statd's main loop busy serving them.  Other datastore users queue up
behind it.

Fork the snapshot instead, renamed statd-journal using prctl(), running
at nice 10 with its own sysrepo connection.  The dump is chunked, one
module per sr_get_data() call with a short breather in between, so
interactive users interleave with the snapshot instead of waiting for
all of it.  The snapshot timer is one-shot, re-armed only when the
previous snapshot has finished: snapshots can never overlap, and the
interval is the rest between them rather than a fixed cadence.

Each module read waits at most ten seconds: reading operational data
holds the module's read lock, also while waiting for providers, so on
a busy system the journal skips that module for the round instead of
holding up configuration changes:

    Failed sending edit-config RPC: Locking a rwlock failed (sr_shmmod_lock:
    Connection timed out), read lock held by running process 28334 (CID 85),
    running process 3627 (CID 3).  Retrying ...

Skipped modules are counted in the snapshot completion log message.

Also drops the last thread from statd, and the second sysrepo session,
which was only used by the journal.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A failing yanger invocation logged only:

    statd[3539]: Error, running yanger
    statd[3539]: Error adding interface yanger data

leaving no trace of which model or interface failed, or how.  Include
the model, the interface for per-interface queries, and yanger's exit
code.

Also plug a small memory leak: the extracted interface name was never
freed after the query.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Minimal images log the following on every boot and initctl reload,
e.g., when the regression test framework reconfigures the system:

    finit[1]: Skipping /etc/finit.d/enabled/netbrowse.conf, dangling symlink: No such file or directory
    finit[1]: service_register():/etc/finit.d/enabled/ttyd.conf: skipping ttyd: No such file or directory
    finit[1]: Skipping /etc/finit.d/enabled/webui.conf, dangling symlink: No such file or directory

The web services are enabled in the default configuration, so confd's
finit_enable() creates enabled/ symlinks also on images where the
service was never installed.  svc_enable() already guards its nginx
symlinks with the corresponding check.  The ttyd case differs: its
conf ships unconditionally in the common rootfs skeleton while the
daemon itself is an optional package.

Skip enable, with a log message at INFO, when the service conf is not
available in the image.  At build time, drop the ttyd confs when ttyd
is not selected, and prune any dangling enabled/*.conf symlinks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
-v info mapped to LOG_NOTICE, making actual INFO level messages
unreachable from the command line.  -v debug opened the log mask but
not the DEBUG() macro, which is gated on the debug variable, so debug
messages still required the DEBUG environment variable to be set.

Same behavior as statd.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
@troglobit
troglobit marked this pull request as ready for review August 3, 2026 13:14
@troglobit
troglobit requested a review from mattiaswal August 3, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:main Build default defconfig, not minimal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants